package com.LachesismStorm.lachesism.member.service.impl;

import com.LachesismStorm.lachesism.core.util.AsyncUtil;
import com.LachesismStorm.lachesism.member.vo.Download;
import com.LachesismStorm.lachesism.member.mapper.WebpageMapper;
import com.LachesismStorm.lachesism.member.service.WebpageService;
import com.aayushatharva.brotli4j.Brotli4jLoader;
import com.aayushatharva.brotli4j.decoder.BrotliInputStream;
import lombok.SneakyThrows;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Response;
import org.json.JSONArray;
import org.json.JSONObject;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Document;
import org.jsoup.nodes.Element;
import org.jsoup.select.Elements;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.net.HttpURLConnection;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardOpenOption;
import java.util.*;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * @Author lachesism
 * @Date 2025-03-09 18:57
 **/

@Service
public class WebpageServiceImpl implements WebpageService {
    private static final int MAX_RETRIES = 5;
    private static final int THREAD_POOL_SIZE = 5; // 线程池大小

    private static final String DOWNLOAD_PATH = "/Users/yuno/Downloads/x1/";

    private final OkHttpClient client = new OkHttpClient();


    @Resource
    private WebpageMapper webpageMapper;


    private List<String> getDownloadedUrl() {
        return webpageMapper.getDownloadedUrl();
    }

    // 1. 提取 HTML 中的链接
    // 2. 构建 Download 对象
    // 3. 获取已下载 URL 列表
    // 4. 过滤掉已下载的 -> 得到 filteredDownloads
    // 5. ✅ 写入文件
    // 6. ✅ 插入数据库
    @Override
    public void getXLinks() throws IOException {
        String inputHtmlPath = "/Users/yuno/java/code/LachesismStorm/LachesismStorm/lachesism-service/src/main/resources/html/temp.html";
        String outputFilePath = "/Users/yuno/Downloads/x1/link.txt";
        LinkedList<Download> downloads = new LinkedList<>();

        // 使用 Set 去重
        Set<String> uniqueLinks = new HashSet<>();

        // 解析 HTML，提取视频链接
        try {
            Document doc = Jsoup.parse(new File(inputHtmlPath), "UTF-8");
            Elements links = doc.select("[id^=verticalGridItem-][id$=-profile-grid-0] > div > div > div > a");

            for (Element link : links) {
                String href = link.attr("href");
                href = "https://x.com" + href;
                System.out.println(href);

                if (!href.contains("/photo")) {
                    uniqueLinks.add(href.replaceAll("/video/1", ""));
                }
            }
        } catch (IOException e) {
            System.err.println("Error extracting video links from HTML.");
            e.printStackTrace();
        }

        // 确保输出文件存在
        ensureFileExists(outputFilePath);

        // 读取文件中已有链接，合并去重
        try {
            uniqueLinks.addAll(Files.readAllLines(Paths.get(outputFilePath), StandardCharsets.UTF_8));
        } catch (IOException e) {
            System.err.println("Error reading existing links from file.");
            e.printStackTrace();
        }

        Brotli4jLoader.ensureAvailability();

        // 将链接变为 Download 对象列表
        for (String videoUrl : uniqueLinks) {
            try {
                String response = fetchVideoInfo(videoUrl);
                if (response != null) {
                    Download download = parseVideoUrl(response);
                    int start = videoUrl.indexOf("x.com/") + 6;
                    int end = videoUrl.indexOf("/status");
                    download.setAuthor(videoUrl.substring(start, end));
                    download.setUrl(videoUrl);
                    downloads.add(download);
                }
            } catch (Exception e) {
                System.err.println("Error processing URL: " + videoUrl);
                e.printStackTrace();
            }
        }

        // 获取数据库中已下载的链接
        List<String> downloadedUrl = getDownloadedUrl();

        // 过滤掉已下载的链接
        List<Download> newDownloads = downloads.stream()
                .filter(d -> !downloadedUrl.contains(d.getUrl()))
                .collect(Collectors.toList());

        // ✅ 写入未下载过的链接到文件
        try (BufferedWriter writer = Files.newBufferedWriter(Paths.get(outputFilePath), StandardCharsets.UTF_8)) {
            for (Download d : newDownloads) {
                writer.write(d.getUrl());
                writer.newLine();
            }
        } catch (IOException e) {
            System.err.println("Error writing deduplicated links.");
            e.printStackTrace();
        }

        // ✅ 插入未下载的新数据到数据库
        webpageMapper.download(newDownloads);
    }

    @Override
    public void download() {
        List<Download> downloadList = webpageMapper.getDownload();
        ExecutorService executor = Executors.newFixedThreadPool(THREAD_POOL_SIZE);
        for (Download download : downloadList) {
            executor.submit(() -> downloadFile(download));
        }
        executor.shutdown();
        try {
            executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
        } catch (InterruptedException e) {
            System.err.println("Download interrupted: " + e.getMessage());
        }
        webpageMapper.batchUpdate(downloadList);
    }


    private void downloadFile(Download download) {
        String desc = sanitizeFileName(download.getDescription());
        String downloadUrl = download.getDownloadUrl();
        Path filePath = Paths.get(DOWNLOAD_PATH, desc + ".mp4");

        boolean success = false;
        int attempts = 0;

        while (!success && attempts < MAX_RETRIES) {
            attempts++;
            try {
                System.out.println("正在下载 " + desc + ".mp4（第 " + attempts + " 次尝试）...");

                Request request = new Request.Builder().url(downloadUrl).build();
                try (Response response = client.newCall(request).execute()) {
                    if (response.isSuccessful() && response.body() != null) {
                        saveFile(response.body().byteStream(), filePath);
                        System.out.println("下载完成：" + desc + ".mp4");
                        success = true;
                    } else {
                        System.err.println("下载失败：" + desc + ".mp4（HTTP 状态码：" + response.code() + "）");
                    }
                }
            } catch (Exception e) {
                System.err.println("下载 " + desc + ".mp4 时发生错误：" + e.getMessage());
            }

            if (!success && attempts < MAX_RETRIES) {
                try {
                    Thread.sleep(200); // 等待 0.2 秒后重试
                } catch (InterruptedException ignored) {
                }
            }
        }

        if (!success) {
            System.err.println("下载 " + desc + ".mp4 失败，已尝试 " + MAX_RETRIES + " 次。");
        }
    }

    private String sanitizeFileName(String name) {
        if (name == null || name.trim().isEmpty()) {
            return "default_filename"; // 避免空文件名
        }
        return name.replaceAll("[\\\\/:*?\"<>|\\s\\r\\n]", ""); // 过滤非法字符、空格和换行符
    }

    private void saveFile(InputStream inputStream, Path filePath) throws IOException {
        Files.createDirectories(filePath.getParent()); // 确保目录存在
        try (OutputStream outputStream = Files.newOutputStream(filePath, StandardOpenOption.CREATE, StandardOpenOption.TRUNCATE_EXISTING)) {
            byte[] buffer = new byte[8192];
            int bytesRead;
            while ((bytesRead = inputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, bytesRead);
            }
        }
    }


    /**
     * 解析视频信息
     */
    private Download parseVideoUrl(String jsonResponse) {
        Download download = new Download();
        JSONObject jsonObject = new JSONObject(jsonResponse);
        download.setDescription(jsonObject.optString("title"));

        JSONArray videoInfos = jsonObject.optJSONArray("videoInfos");
        if (videoInfos != null && videoInfos.length() > 0) {
            JSONObject videoData = videoInfos.getJSONObject(videoInfos.length() - 1);
            download.setDownloadUrl(videoData.optString("url"));
        }

        return download;
    }

    /**
     * 获取视频 JSON 信息
     */
    private String fetchVideoInfo(String videoUrl) throws IOException {
        String jsonData = String.format("{\"url\":\"%s\"}", videoUrl);
        int maxRetries = 10;
        int retryCount = 0;

        while (retryCount < maxRetries) {
            HttpURLConnection con = (HttpURLConnection) new URL("https://download-x-video.com/api/parse").openConnection();
            con.setRequestMethod("POST");
            con.setRequestProperty("Content-Type", "application/json");
            con.setRequestProperty("User-Agent", "Mozilla/5.0 (Windows NT 10.0; Win64; x64)");
            con.setRequestProperty("Accept-Encoding", "gzip, deflate, br");
            con.setDoOutput(true);

            // 发送 JSON 数据
            try (OutputStream os = con.getOutputStream()) {
                os.write(jsonData.getBytes(StandardCharsets.UTF_8));
            }

            int responseCode = con.getResponseCode();

            if (responseCode == 400) {
                retryCount++;
                System.out.println("请求返回 400 错误 , 重试 " + retryCount + "/" + maxRetries);
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                }
                continue;
            }

            // 解析 Brotli 或普通输入流
            try (InputStream inputStream = "br".equalsIgnoreCase(con.getContentEncoding())
                    ? new BrotliInputStream(con.getInputStream())
                    : con.getInputStream();
                 BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream, StandardCharsets.UTF_8))) {
                return reader.lines().collect(Collectors.joining());
            }
        }

        throw new IOException("经过" + maxRetries + "次尝试后仍然无法获取视频信息");
    }

    /**
     * 确保文件存在
     */
    private void ensureFileExists(String filePath) {
        File file = new File(filePath);
        if (!file.exists()) {
            try {
                if (file.getParentFile() != null) {
                    file.getParentFile().mkdirs();
                }
                file.createNewFile();
            } catch (IOException e) {
                System.err.println("创建文件错误: " + filePath);
                e.printStackTrace();
            }
        }
    }
}
